package com.sh.cloudrender.domian.service.kube;

import com.alibaba.druid.support.json.JSONUtils;
import io.kubernetes.client.custom.Quantity;
import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.AppsV1Api;
import io.kubernetes.client.openapi.models.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
public class DeployService {

    @Autowired
    private AppsV1Api appsV1Api;

    /**
     * 创建pod的deployment
     *
     * @param uid
     * @param namespace
     * @param image
     * @return
     */
    public V1Deployment createOrUpdateDeployment(String uid, String namespace, String image, Integer replicas, String cpu,
                                                 String memory, boolean enableGpu,Integer containerPort) {
        V1Deployment deployment = getDeployment(uid, namespace);

        // Add 添加容器端口 weiqing.hk
        containerPort = ( null == containerPort) ? 3000 : containerPort;
        List<V1ContainerPort> ports = new ArrayList<>(1);
        V1ContainerPort v1ContainerPort = new V1ContainerPort();
        v1ContainerPort.setContainerPort(containerPort);
        ports.add(v1ContainerPort);

        if (deployment != null) {
            String deploymentJson = appsV1Api.getApiClient().getJSON().serialize(deployment);
            V1ObjectMeta updatedMetadata = deployment.getMetadata();
            updatedMetadata.putAnnotationsItem("kubectl.kubernetes.io/last-applied-configuration", deploymentJson);
            deployment.setMetadata(updatedMetadata);
            V1Container v1Container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0);
            v1Container.setImage(image);
            deployment.getSpec().setReplicas(replicas);

            // Set resource requirements if specified
            if (!cpu.equals("0") || !memory.equals("0")) {
                V1ResourceRequirements resources = new V1ResourceRequirements();
                if (!cpu.equals("0")) {
                    resources.putRequestsItem("cpu", new Quantity(cpu));
                    resources.putLimitsItem("cpu", new Quantity(cpu));
                }
                if (!memory.equals("0")) {
                    resources.putRequestsItem("memory", new Quantity(memory + "Gi"));
                    resources.putLimitsItem("memory", new Quantity(memory + "Gi"));
                }
                /*
                 TODO 这里调协为狡占模式后，只能启动固定的数量，应该默认设置为运动时 gpu模式即可
                if (enableGpu) {
                    resources.putRequestsItem("nvidia.com/gpu", new Quantity("1"));
                    resources.putLimitsItem("nvidia.com/gpu", new Quantity("1"));
                }*/
                v1Container.setResources(resources);
                v1Container.setPorts(ports);
                log.info("set container port : {} ", JSONUtils.toJSONString(containerPort));
            }

            if (enableGpu) {
                // Set node selector for GPU nodes
                deployment.getSpec().getTemplate().getSpec().getNodeSelector().put("kubernetes.io/gpu", "true");
            }

            try {
                V1Deployment updatedDeployment = appsV1Api.replaceNamespacedDeployment(uid, namespace, deployment, null, null, null, null);
                return updatedDeployment;
            } catch (ApiException e) {
                log.error("uid:{} namespace:{} already has deployment, replace it error, msg:{}", uid, namespace, e.getResponseBody());
                return null;
            }
        }

        // Create a new deployment with resource requirements if specified
        V1ResourceRequirements resources = null;
        if (!cpu.equals("0") || !memory.equals("0") || enableGpu) {
            resources = new V1ResourceRequirements();
            if (!cpu.equals("0")) {
                resources.putRequestsItem("cpu", new Quantity(cpu));
                resources.putLimitsItem("cpu", new Quantity(cpu));
            }
            if (!memory.equals("0")) {
                resources.putRequestsItem("memory", new Quantity(memory + "Gi"));
                resources.putLimitsItem("memory", new Quantity(memory + "Gi"));
            }
           /*
           TODO 这里调协为狡占模式后，只能启动固定的数量，应该默认设置为运动时 gpu模式即可
           if (enableGpu) {
                resources.putRequestsItem("nvidia.com/gpu", new Quantity("1"));
                resources.putLimitsItem("nvidia.com/gpu", new Quantity("1"));
            }
           */
        }

        V1Container container = new V1Container()
                .name(uid)
                .image(image)
                .imagePullPolicy("IfNotPresent");
        // container ports
        container.setPorts(ports);
        //TODO判断暂时不需要写死了 .command(Arrays.asList("./entrypoint.sh"))

        if (resources != null) {
            container.setResources(resources);
        }

        V1PodSpec podSpec = new V1PodSpec().containers(Collections.singletonList(container));

        if (enableGpu) {
            // Set node selector for GPU nodes
            podSpec.setNodeSelector(Collections.singletonMap("kubernetes.io/gpu", "true"));
        }

        V1Deployment deploymentBuilder = new V1DeploymentBuilder()
                .withApiVersion("apps/v1")
                .withKind("Deployment")
                .withMetadata(new V1ObjectMeta().name(uid).namespace(namespace).putLabelsItem("uid", uid))
                .withSpec(
                        new V1DeploymentSpec()
                                .replicas(replicas)
                                .selector(new V1LabelSelector().putMatchLabelsItem("uid", uid))
                                .template(
                                        new V1PodTemplateSpec()
                                                .metadata(new V1ObjectMeta().putLabelsItem("uid", uid))
                                                .spec(podSpec)
                                )
                ).build();

        V1Deployment resp;
        V1Deployment newDeployment = deploymentBuilder;
        try {
            resp = appsV1Api.createNamespacedDeployment(namespace, newDeployment,
                    null, null, null, null);
        } catch (ApiException e) {
            log.error("uid:{} namespace:{} apply deployment failed, msg:{}", uid, namespace, e.getResponseBody());
            return null;
        }
        return resp;
    }

    /**
     * 删除deployment
     *
     * @param uid
     * @param namespace
     * @return
     */
    public boolean deleteDeployment(String uid, String namespace) {
        V1DeleteOptions deleteOptions = new V1DeleteOptions();
        deleteOptions.setPropagationPolicy("Foreground");
        V1Status v1Status = null;
        try {
            v1Status = appsV1Api.deleteNamespacedDeployment(uid, namespace, null, null,
                    null, null, null, deleteOptions);
        } catch (ApiException e) {
            log.error("delete uid:{} namespace:{} deployment error, msg:{}", uid, namespace, e.getResponseBody());
            return false;
        }

        return true;
    }

    /**
     * @param uid
     * @param namespace
     * @param replicas
     * @return
     */
    public boolean scaleDeploymentPods(String uid, String namespace, Integer replicas) {
        V1Scale scale = null;
        try {
            scale = appsV1Api.readNamespacedDeploymentScale(uid, namespace, null);
            if (scale == null) {
                log.warn("uid:{} namespace:{} has no deployment, pls create deployment before scale pods", uid, namespace);
                return false;
            }
        } catch (ApiException e) {
            log.warn("uid:{} namespace:{} has no deployment, pls create deployment before scale pods", uid, namespace);
            return false;
        }

        scale.getSpec().setReplicas(replicas);
        V1Scale resp = null;
        try {
            resp = appsV1Api.replaceNamespacedDeploymentScale(uid, namespace, scale, null, null, null, null);
            if (resp == null) {
                return false;
            }
        } catch (ApiException e) {
            log.error("scale uid:{} namespace:{} deployment pods error, msg:{}", uid, namespace, e.getResponseBody());
            return false;
        }

        return true;
    }


    /**
     * 查询某个用户的deployment
     *
     * @param uid
     * @param namespace
     * @return
     */
    public V1Deployment getDeployment(String uid, String namespace) {
        V1Deployment v1Deployment = null;
        try {
            v1Deployment = appsV1Api.readNamespacedDeployment(uid, namespace, null);
        } catch (ApiException e) {

        }
        return v1Deployment;
    }

    public boolean exist(String namespace, String uid) {
        V1Deployment deployment = getDeployment(uid, namespace);
        if (deployment == null) {
            return false;
        }

        return true;
    }
}
